#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <unistd.h>
#include <time.h>
#include "srtp.h"

    /******* Client Functions *****/

    // Initialize port_num to hold the port number given 
    int port_num;  

    // Initialize destination address with length of 32
    char dest_address[32];

    // Initialize pcb
    struct srtp_client_pcb x; 

    // sockaddr_in for server info
    struct sockaddr_in serv_info;
    struct sockaddr_in serv_info1;

    // sockaddr_in for client info
    struct sockaddr_in client_listen_info;

    // Temporary buffer for data to send
    char buffer[SRTP_MAX_SEND_BUF_SIZE];     

    // socklen_t holds size of the socket
    socklen_t fSockLen;

    // Initialize a socket for the client
    int socket_client;

    // pktbuf of the syn packet
    struct pktbuf syn_packet;

    // hdr for the syn packet
    struct srtp_hdr hdr;

    // pktbuf of the syn_ack packet from the server
    struct pktbuf client_temp;

    // size_t of the client_datagram
    size_t client_datagram;

    // Holds filename of file to transfer
    char filename[200];

    // Initialize a FILE for the file to transfer
    FILE * pFile;

    // Initialize pktbuf of data packet
    struct pktbuf send_buffer;

    // pktbuf that holds the ack sent back from the server
    struct pktbuf ack_temp; 

    // holds the current sequence number that the client has sent
    int current_seq = 1;

    // Used to keep track of number of packets already sent and break out of sending loop if problems occur
    int j = 0;

    // Used to keep track of number of lost packets and break out of resending loop if problems occur
    int lost_packet_count = 0;

    // Used to store the last lost packet number, for debugging purposes
    int last_lost_packet = 0;

    // 
    int sent_counter = 1;

    int test_mode = 0;

/*
 Function:   srtp_connect
 Purpose:    Called by the client to establish a connection to a server. 
 Performs 2-way connection establishment handshake. 
 In case of success, the client PCP structure (cpcb) is initialised.
 Parameters: 
 cpcb	: client PCB 
 dst_addr: string that represents server IP address, e.g. "192.168.2.33" 
 dst_port: server port number
 
 
 Returns:     0 on success, -1 on failure
 */

int srtp_connect(struct srtp_client_pcb *cpcb, char *dst_addr, int dst_port){
    
    // Creating a socket
    socket_client = socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
    
    // Passing sockfd info to cpcb
    x.sockfd = socket_client;
    
    // Set Serv_info details
    serv_info.sin_family = AF_INET;
    inet_aton(x.destination_IP, &(serv_info.sin_addr)); 
    serv_info.sin_port =htons(x.destination_port);
    memset(&(serv_info.sin_zero),'\0',8);
    
    // Set listen_info details
    client_listen_info.sin_family = AF_INET;
    client_listen_info.sin_addr.s_addr = INADDR_ANY; 
    client_listen_info.sin_port = htons(x.destination_port);
    bzero(&(client_listen_info.sin_zero),8);
    fSockLen = sizeof(client_listen_info);
    
    // Binds the socket
    int bind_call;
    bind_call = bind(x.sockfd,(struct sockaddr*)&client_listen_info, sizeof(struct sockaddr));
    if (bind_call < 0){
        perror("|| On binding socket");
    }
    
    // Creating header for the syn packet 
    hdr.type = 3; 
    hdr.seqnbr = 0;
    
    // Filling in syn packet with info
    syn_packet.hdr =hdr;
    syn_packet.len = 0; 
    syn_packet.payload[0];
    
    
    /*
        While the state is not connected,
            After sending the SYN packet,
            We try to receive a SYN_ACK for 2 seconds,
            If there is nothing to read,
                We then resend the SYN packet,
                    After 3 tries, we give up and send a RST and close the connection. -- This needs to be done
     */
    
    int f = 0;
    
    while ( x.state != 1 ) {
        
        if ( f == 4 ) {
            printf("|| SYN resent 3 times with no SYN_ACK, Giving Up...\n\n");
            exit(1);
        }
        
        // Sending SYN packet 
        int connect_send_syn = sendto(socket_client, &syn_packet, sizeof(struct pktbuf), 0,(struct sockaddr *)&serv_info, sizeof(serv_info));
        
        if (connect_send_syn < 0) {
            perror("|| On sending SYN");
            exit(EXIT_FAILURE);
        } else {
            
            // Debug code that prints the buffer contents
            // printf("** Sent %s\n", x.buffer_pcb); 
            
            // Changing the state to 3 after sending syn
            x.state = 3;
            printf(">> The state is now SYN_SENT - %d\n", x.state);
            
        } 
        
        //printf("** waiting 3...\n");
        
        /*  
            Receiving SYN_ACK
            using SELECT we monitor the socket for anything to read, 
            if there is nothing to read after the timeout timeval, 
            we retry 3 times and then give up and reset.
        */
        struct timeval timeout;
        
        timeout.tv_sec = 2;
        timeout.tv_usec = 0;
        
        fd_set readfds, masterfds;
        
        FD_ZERO(&masterfds);
        FD_SET(x.sockfd, &masterfds);
        
        memcpy(&readfds, &masterfds, sizeof(fd_set));
        
        printf(">> Waiting for SYN_ACK...\n");
        
        if (select(x.sockfd+1, &readfds, NULL, NULL, &timeout) < 0) {
            perror("|| On SYN select");
            exit(EXIT_FAILURE);
        }
        
        
        if (FD_ISSET(x.sockfd, &readfds)) {
            
            client_datagram = recvfrom(x.sockfd, (struct pktbuf *)&client_temp, SRTP_MAX_SEND_BUF_SIZE, 0, (struct sockaddr *)&serv_info1, &fSockLen);
            
            if (client_datagram == -1) {
                // Error handling		
                perror("|| On SYN_ACK recvfrom");
                exit(EXIT_FAILURE);
            }
            else{
               // printf("** Type recieved : %d\n", (int)client_temp.serv_hdr.type );
                
                if ( (int)client_temp.serv_hdr.type == 2 ) {
                    
                    printf(">> SYN_ACK Received...\n");
                    //changing the state to 1 after receiving ack
                    x.state = 1;
                    f = 0;
                    //printf("the state for the client is now %d\n", x.state);
                    
                } else {
                    printf("|| Received something... not an ACK... Resending SYN...");
                    f += 1;
                }
                
                
            }
        } else {
            // Socket timed out
            printf("|| No SYN_ACK... resending SYN...\n");
            f += 1;
            //srtp_client_reset(&x);        
            // srtp_connect(&x, dest_address, port_num);
        }  
    }
}


/*
 Function:    srtp_close
 Purpose:     Called by the client to terminate a connection. 
 Performs 2-way connection termination handshake. 
 The client PCP structure (cpcb) is updated accordingly, i.e. state -> Closed.
 
 Parameters: 
 cpcb	: client PCB 
 
 Returns:     0 on success, -1 on failure
 */
int srtp_close(struct srtp_client_pcb *cpcb){
    
    // Creating header for the FIN packet
    hdr.type = 4;
    hdr.seqnbr = 0;
    
    // Filling in FIN packet with info
    syn_packet.hdr =hdr;
    syn_packet.len = 0; 
    syn_packet.payload[0];
    
    // Seinding the FIN packet 
    int connect_send_syn = sendto(socket_client, &syn_packet, sizeof(struct pktbuf), 0,(struct sockaddr *)&serv_info, sizeof(serv_info));
    
    if (connect_send_syn < 0) {
        //check for error
        perror("|| On FIN sendto");
        exit(EXIT_FAILURE);
	}
	else {
        // printf("Sent 2 %s\n", x.buffer_pcb); 
        //changing the state to 4 after sending FIN
        x.state = 4;
        printf(">> The state is now FIN_WAIT - %d\n", x.state);	  
	} 
    
    //receiving
	//define client_datagram as info received from server
    client_datagram = recvfrom(x.sockfd, (struct pktbuf *)&client_temp, SRTP_MAX_PAYLOAD_SIZE, 0, (struct sockaddr *)&serv_info1, &fSockLen);
	
	if (client_datagram == -1) {
        // Error handling
		perror("|| On FIN_ACK recvfrom");
		exit(EXIT_FAILURE);
	}
	else{
        // printf("type recieved : %lu\n", client_temp.serv_hdr.type );
        //	printf("Datagram length : %lu\n", (long unsigned)client_datagram );
        // changing the state to 1 after receiving ack
		x.state = 2;
		printf(">> The state is now CLOSED - %d\n", x.state);
		//terminate the program
		exit(0);
	}
}


/*
 Function:	srtp_send
 Purpose:	Called by the client application to send a data packet to the server. 
 The maximum number of bytes the function accepts is SRTP_MAX_PAYLOAD_SIZE.
 
 Since our protocol is implemented at the application layer, we cannot rely
 on the kernel to do all the asyncronoys ARQ processing such as receiving and
 processing of ACKs, managing time-outs, retransmitting data packets etc.
 
 The function therefore needs to provide functionality such as the following:
 - Receiving and processing of ACKs
 - If there is space in the send buffer, put data packet into send buffer,
 send it over the network, and start a corresponding timer.
 - If the send buffer is already full, the function should sleep for a short
 amount of time, and then see if there are any new ACKs that would result in
 freed up space in the send buffer.
 - The function also needs to check of there are any retransmission timeouts, and
 needs to do the necessary retransmissions, and update the timers.  
 - ...
 
 Parameters: 
 cpcb	: client PCB  
 data	: pointer to buffer with data to send
 len		: number of bytes to send
 
 Returns:     0 on success, -1 on failure
 */
int srtp_send(struct srtp_client_pcb *cpcb, char data, int len){
    
    
    // Creating header for the DATA packet
    hdr.type = 1;
    hdr.seqnbr = current_seq;
    
    // Filling in FIN packet with info
    send_buffer.hdr = hdr;
    send_buffer.len = len;
    strncpy(send_buffer.payload, x.buffer_pcb, len-1);
    
    //printf("Buffer contains :\n %s\n END....\n", send_buffer.payload);
    //fflush(stdout);
    
    //printf("** Check point 2\n");
    
    // Test Code
    
    if ( test_mode == 1 ) {
        
        if ( sent_counter % 5 == 0 ) {
            
            // drops a packet
            printf(">> Simulating a dropped data packet\n");
            /* 
             Receiving ACK for DATA packet
             If no ACK is received,
             Increase lost_packet_count by 1 and resend
             When lost_packet_count is 11 ( 10 lost packets )
             Break out of the sending loop and show details
             */
            
            // Z is used to keep the loop working until one of the break conditions are met.
            int z = 0;
            
            //printf("** Waiting 2...\n");	
            
            while( z != 1){
                
                //printf("** Waiting 3...\n");	
                
                if ( lost_packet_count == 11 ) {
                    
                    // Break out after 10 retries
                    z = 1; 
                    
                }
                
                /*  
                 Receiving DATA_ACK
                 using SELECT we monitor the socket for anything to read, 
                 if there is nothing to read after the timeout timeval, 
                 we retry 10 times and then give up and reset.
                 */
                
                
                struct timeval timeout;
                
                timeout.tv_sec = 10;
                timeout.tv_usec = 0;
                
                fd_set readfds, masterfds;
                
                FD_ZERO(&masterfds);
                FD_SET(x.sockfd, &masterfds);
                
                memcpy(&readfds, &masterfds, sizeof(fd_set));
                
                printf(">> Waiting for DATA ACK...\n");
                
                if (select(x.sockfd+1, &readfds, NULL, NULL, &timeout) < 0) {
                    perror("|| On DATA_ACK Select");
                    exit(EXIT_FAILURE);
                }
                
                // printf("** waiting 4...\n");
                
                if (FD_ISSET(x.sockfd, &readfds)) {
                    // read from socket
                    client_datagram = recvfrom(x.sockfd, (struct pktbuf *)&ack_temp, SRTP_MAX_SEND_BUF_SIZE, 0, (struct sockaddr *)&serv_info1, &fSockLen);
                    
                    if (client_datagram == -1) {
                        //check for error
                        perror("|| On DATA_ACK recvfrom");
                        exit(EXIT_FAILURE);
                    }
                    
                    printf(">> Payload : %lu\n", (long unsigned)client_datagram);
                    
                    // printf("** ack temp hdr type : %d\n", ack_temp.serv_hdr.type);
                    // printf("** ack temp hdr seqnbr : %d\n", ack_temp.serv_hdr.seqnbr);
                    
                    printf(">> Finished Waiting...\n");
                    if ( ack_temp.serv_hdr.type == 2 && ack_temp.serv_hdr.seqnbr == current_seq ){
                        printf(">> ACK is... GOOD... ack_seq = %d\n", ack_temp.serv_hdr.seqnbr);
                        current_seq += 1;
                        
                        lost_packet_count = 0;
                        j++;
                        z = 1;
                    } else {
                        printf(">> ACK is... BAD... resending\n");
                        printf(">> Number of lost packets : %d\n", lost_packet_count);
                        lost_packet_count++;
                        z = 1;
                    }
                    
                } else {
                    // socket timed out
                    printf(">> No ACK... resending...\n");
                    printf(">> Number of lost packets : %d\n", lost_packet_count);
                    lost_packet_count++;
                    z = 1;
                }  
                
            }
            
            sent_counter += 1;
            
        } else if ( sent_counter % 8 == 0 ) {
            
            // sends a wrong seqnum packet
            printf(">> Simulating an out of order packet\n");
            
            struct pktbuf wrong_order;
            struct srtp_hdr wrong_order_hdr;
            
            wrong_order_hdr.type = 1;
            wrong_order_hdr.seqnbr = 2;
            
            wrong_order.hdr = wrong_order_hdr;
            wrong_order.len = len;
            wrong_order.payload[0] = 0;
            
            // sending
            int send_data = sendto(socket_client, &wrong_order, sizeof(struct pktbuf), 0,(struct sockaddr *)&serv_info, sizeof(serv_info));
            
            sent_counter += 1;
            
            if (send_data < 0) {
                //check for error
                perror("** On testmode wrongorder sendto");
                exit(EXIT_FAILURE);
            }
            else {
                // printf("Sent %s\n", send_buffer.payload); 
                // changing the state to 1 after sendnig 
                // printf(">> Sent seqnbr : %d\n", send_buffer.hdr.seqnbr);
                printf(">> Current seqnbr : %d\n", current_seq);
                
                //x.state = 1;
                //fflush(stdout);
                //printf(">> The state is now %d\n", x.state);
                //setbuf(stdout,NULL);
                //printf("** Waiting...\n");	 
            }
            /* 
             Receiving ACK for DATA packet
             If no ACK is received,
             Increase lost_packet_count by 1 and resend
             When lost_packet_count is 11 ( 10 lost packets )
             Break out of the sending loop and show details
             */
            
            // Z is used to keep the loop working until one of the break conditions are met.
            int z = 0;
            
            //printf("** Waiting 2...\n");	
            
            while( z != 1){
                
                //printf("** Waiting 3...\n");	
                
                if ( lost_packet_count == 11 ) {
                    
                    // Break out after 10 retries
                    z = 1; 
                    
                }
                
                /*  
                 Receiving DATA_ACK
                 using SELECT we monitor the socket for anything to read, 
                 if there is nothing to read after the timeout timeval, 
                 we retry 10 times and then give up and reset.
                 */
                
                
                struct timeval timeout;
                
                timeout.tv_sec = 10;
                timeout.tv_usec = 0;
                
                fd_set readfds, masterfds;
                
                FD_ZERO(&masterfds);
                FD_SET(x.sockfd, &masterfds);
                
                memcpy(&readfds, &masterfds, sizeof(fd_set));
                
                if (select(x.sockfd+1, &readfds, NULL, NULL, &timeout) < 0) {
                    perror("|| On DATA_ACK Select");
                    exit(EXIT_FAILURE);
                }
                
                // printf("** waiting 4...\n");
                
                if (FD_ISSET(x.sockfd, &readfds)) {
                    // read from socket
                    client_datagram = recvfrom(x.sockfd, (struct pktbuf *)&ack_temp, SRTP_MAX_SEND_BUF_SIZE, 0, (struct sockaddr *)&serv_info1, &fSockLen);
                    
                    if (client_datagram == -1) {
                        //check for error
                        perror("|| On DATA_ACK recvfrom");
                        exit(EXIT_FAILURE);
                    }
                    
                    printf(">> Payload : %lu\n", (long unsigned)client_datagram);
                    
                    // printf("** ack temp hdr type : %d\n", ack_temp.serv_hdr.type);
                    // printf("** ack temp hdr seqnbr : %d\n", ack_temp.serv_hdr.seqnbr);
                    
                    printf(">> Finished Waiting...\n");
                    if ( ack_temp.serv_hdr.type == 2 && ack_temp.serv_hdr.seqnbr == current_seq ){
                        printf(">> ACK is... GOOD... ack_seq = %d\n", ack_temp.serv_hdr.seqnbr);
                        current_seq += 1;
                        
                        lost_packet_count = 0;
                        j++;
                        z = 1;
                    } else {
                        printf(">> ACK is... BAD... resending\n");
                        printf(">> Number of lost packets : %d\n", lost_packet_count);
                        lost_packet_count++;
                        z = 1;
                    }
                    
                } else {
                    // socket timed out
                    printf(">> No ACK... resending...\n");
                    printf(">> Number of lost packets : %d\n", lost_packet_count);
                    lost_packet_count++;
                    z = 1;
                }  
                
            }
            
        } else {
            // sending
            int send_data = sendto(socket_client, &send_buffer, sizeof(struct pktbuf), 0,(struct sockaddr *)&serv_info, sizeof(serv_info));
            
            sent_counter += 1;
            
            if (send_data < 0) {
                //check for error
                perror("** On testmode sendto");
                exit(EXIT_FAILURE);
            }
            else {
                // printf("Sent %s\n", send_buffer.payload); 
                // changing the state to 1 after sendnig 
                // printf(">> Sent seqnbr : %d\n", send_buffer.hdr.seqnbr);
                printf(">> Current seqnbr : %d\n", current_seq);
                
                //x.state = 1;
                //fflush(stdout);
                //printf(">> The state is now %d\n", x.state);
                //setbuf(stdout,NULL);
                //printf("** Waiting...\n");	 
            }
            
            /* 
             Receiving ACK for DATA packet
             If no ACK is received,
             Increase lost_packet_count by 1 and resend
             When lost_packet_count is 11 ( 10 lost packets )
             Break out of the sending loop and show details
             */
            
            // Z is used to keep the loop working until one of the break conditions are met.
            int z = 0;
            
            //printf("** Waiting 2...\n");	
            
            while( z != 1){
                
                //printf("** Waiting 3...\n");	
                
                if ( lost_packet_count == 11 ) {
                    
                    // Break out after 10 retries
                    z = 1; 
                    
                }
                
                /*  
                 Receiving DATA_ACK
                 using SELECT we monitor the socket for anything to read, 
                 if there is nothing to read after the timeout timeval, 
                 we retry 10 times and then give up and reset.
                 */
                
                
                struct timeval timeout;
                
                timeout.tv_sec = 10;
                timeout.tv_usec = 0;
                
                fd_set readfds, masterfds;
                
                FD_ZERO(&masterfds);
                FD_SET(x.sockfd, &masterfds);
                
                memcpy(&readfds, &masterfds, sizeof(fd_set));
                
                if (select(x.sockfd+1, &readfds, NULL, NULL, &timeout) < 0) {
                    perror("|| On DATA_ACK Select");
                    exit(EXIT_FAILURE);
                }
                
                // printf("** waiting 4...\n");
                
                if (FD_ISSET(x.sockfd, &readfds)) {
                    // read from socket
                    client_datagram = recvfrom(x.sockfd, (struct pktbuf *)&ack_temp, SRTP_MAX_SEND_BUF_SIZE, 0, (struct sockaddr *)&serv_info1, &fSockLen);
                    
                    if (client_datagram == -1) {
                        //check for error
                        perror("|| On DATA_ACK recvfrom");
                        exit(EXIT_FAILURE);
                    }
                    
                    printf(">> Payload : %lu\n", (long unsigned)client_datagram);
                    
                    // printf("** ack temp hdr type : %d\n", ack_temp.serv_hdr.type);
                    // printf("** ack temp hdr seqnbr : %d\n", ack_temp.serv_hdr.seqnbr);
                    
                    printf(">> Finished Waiting...\n");
                    if ( ack_temp.serv_hdr.type == 2 && ack_temp.serv_hdr.seqnbr == current_seq ){
                        printf(">> ACK is... GOOD... ack_seq = %d\n", ack_temp.serv_hdr.seqnbr);
                        current_seq += 1;
                        
                        lost_packet_count = 0;
                        j++;
                        z = 1;
                    } else {
                        printf(">> ACK is... BAD... resending\n");
                        printf(">> Number of lost packets : %d\n", lost_packet_count);
                        lost_packet_count++;
                        z = 1;
                    }
                    
                } else {
                    // socket timed out
                    printf(">> No ACK... resending...\n");
                    printf(">> Number of lost packets : %d\n", lost_packet_count);
                    lost_packet_count++;
                    z = 1;
                }  
                
            }
        }  
           
    } else {
    
    // sending
    int send_data = sendto(socket_client, &send_buffer, sizeof(struct pktbuf), 0,(struct sockaddr *)&serv_info, sizeof(serv_info));
    
    if (send_data < 0) {
        //check for error
        perror("Error");
        exit(EXIT_FAILURE);
    }
    else {
        // printf("Sent %s\n", send_buffer.payload); 
        // changing the state to 1 after sendnig 
        // printf(">> Sent seqnbr : %d\n", send_buffer.hdr.seqnbr);
        printf(">> Current seqnbr : %d\n", current_seq);
        
        //x.state = 1;
        //fflush(stdout);
        //printf(">> The state is now %d\n", x.state);
        //setbuf(stdout,NULL);
        //printf("** Waiting...\n");	 
        
    }
    

        
        /* 
            Receiving ACK for DATA packet
            If no ACK is received,
                Increase lost_packet_count by 1 and resend
            When lost_packet_count is 11 ( 10 lost packets )
                Break out of the sending loop and show details
         */
        
        // Z is used to keep the loop working until one of the break conditions are met.
        int z = 0;
        
        //printf("** Waiting 2...\n");	
        
        while( z != 1){
        
            //printf("** Waiting 3...\n");	
            
            if ( lost_packet_count == 11 ) {
                
                // Break out after 10 retries
                z = 1; 
                
            }
            
            /*  
             Receiving DATA_ACK
             using SELECT we monitor the socket for anything to read, 
             if there is nothing to read after the timeout timeval, 
             we retry 10 times and then give up and reset.
             */

            
            struct timeval timeout;
            
            timeout.tv_sec = 10;
            timeout.tv_usec = 0;
            
            fd_set readfds, masterfds;
            
            FD_ZERO(&masterfds);
            FD_SET(x.sockfd, &masterfds);
            
            memcpy(&readfds, &masterfds, sizeof(fd_set));
            
            if (select(x.sockfd+1, &readfds, NULL, NULL, &timeout) < 0) {
                perror("|| On DATA_ACK Select");
                exit(EXIT_FAILURE);
            }
            
            // printf("** waiting 4...\n");
            
            if (FD_ISSET(x.sockfd, &readfds)) {
                // read from socket
                client_datagram = recvfrom(x.sockfd, (struct pktbuf *)&ack_temp, SRTP_MAX_SEND_BUF_SIZE, 0, (struct sockaddr *)&serv_info1, &fSockLen);
                
                if (client_datagram == -1) {
                    //check for error
                    perror("|| On DATA_ACK recvfrom");
                    exit(EXIT_FAILURE);
                }
                
                printf(">> Payload : %lu\n", (long unsigned)client_datagram);
                
                // printf("** ack temp hdr type : %d\n", ack_temp.serv_hdr.type);
                // printf("** ack temp hdr seqnbr : %d\n", ack_temp.serv_hdr.seqnbr);
                
                printf(">> Finished Waiting...\n");
                if ( ack_temp.serv_hdr.type == 2 && ack_temp.serv_hdr.seqnbr == current_seq ){
                    printf(">> ACK is... GOOD... ack_seq = %d\n", ack_temp.serv_hdr.seqnbr);
                    current_seq += 1;
                    
                    lost_packet_count = 0;
                    j++;
                    z = 1;
                } else {
                    printf(">> ACK is... BAD... resending\n");
                    printf(">> Number of lost packets : %d\n", lost_packet_count);
                    lost_packet_count++;
                    z = 1;
                }
                
            } else {
                // socket timed out
                printf(">> No ACK... resending...\n");
                printf(">> Number of lost packets : %d\n", lost_packet_count);
                lost_packet_count++;
                z = 1;
            }  

        }
    }    
}




/*
 Function:    srtp_client_reset
 Purpose:     Called by the client to reset a connection.
 Independent of the state of the connection (except for the CLOSED state), 
 this function sends a RST packet to the server, and resets the connection to the CLOSED state.
 
 Parameters: 
 cpcb	: client PCB 
 
 Returns:     0 on success, -1 on failure
 */
int srtp_client_reset(struct srtp_client_pcb *cpcb);





main(int argc, char *argv[]) { 
    if (argc != 5) {
        
        printf("Usage: \n %s <server> <port> <filename> <test - [0/1]>\n", argv[0]);
        
    } else {
        // Sets current state to CLOSED
        x.state = 2;
        //sent_counter = 0;
        
        // Sets variables from the command line input
        strncpy(dest_address, argv[1], 32-1);
        strncpy(filename, argv[3], 200-1);
        port_num = atoi(argv[2]);
        test_mode = atoi(argv[4]);
        
        printf("-- The destination IP address is, %s\n", dest_address );
        printf("-- The test mode is, %d\n", test_mode);
        //printf("== 0mod5 = %d\n", 0%5);
        // printf("== 10mod5 = %d\n", 10%5);
        // Opens the filename given
        pFile = fopen(filename,"r" );
        if (pFile == NULL){ 
            //error checking
            perror("|| On fopen");
            exit(EXIT_FAILURE);
        }
        
        printf("-- The port number is %d\n", port_num);
        
        x.destination_port = port_num;
        strncpy(x.destination_IP, dest_address, 32-1);
        
        x.buffer_pcb[0] = buffer[0];
        
        // Seek to EOF, to get file size
        fseek(pFile, 0L, SEEK_END);
        int file_size = ftell(pFile);
        
        // printf("** The size of the file is : \n%d\n", file_size);
        
        /* 
            Calculates the number of packets we need to send
            First we find the number of packets
            Then we find the remainder
                If the remainder is more than 0
                    We add one packet to the number of packets
         */
         
        int check = file_size/SRTP_MAX_SEND_BUF_SIZE;
        int check2 = file_size%SRTP_MAX_SEND_BUF_SIZE;
        int check_final;
        
        // printf("** Check is\n%d\n", check);
        // printf("** Check 2 is \n%d\n", check2);
        
        if (check2 > 0){
            check_final= check+1;
        }
        else{
            check_final = check;
        }
        
        printf(">> Number of packets to send : %d\n", check_final);
        // printf("** The buffer is,  %s\n", x.buffer_pcb);
        
        // printf("** The state is %d\n", x.state);
        // printf("** The port is %d\n", x.destination_port);
        
        
        // Calls srtp_connect to establish connection
        srtp_connect(&x, dest_address, port_num);
        
        //printf("** Checkpoint 1\n");
        while (j < check_final){
            
            // Try to send packet 1;
            if ( current_seq == 1 ) {
                file_read(0);
                printf(">> Sent 0\n");
            }
            else {
                file_read((current_seq-1)*SRTP_MAX_PAYLOAD_SIZE);
                
                // This is where we start reading again based on the last sent seqnbr
                int aa = ((current_seq-1)*SRTP_MAX_PAYLOAD_SIZE);
                
                // If this is the final packet zero out the junk after the remainder data as the payload is bigger than the actual data.
                if (current_seq == check_final) {
                    
                    bzero(&x.buffer_pcb[check2], SRTP_MAX_PAYLOAD_SIZE-check2);
                    
                }
                //printf(" >> Sent %s\n\n>>>>", x.buffer_pcb);
                
                printf(">> Sent %d\n", aa);
            }
            
            //strncpy(x.buffer_pcb, buffer, SRTP_MAX_SEND_BUF_SIZE-1);
            
            // calls srtp_send to send the file
            srtp_send(&x, htonl((unsigned long)x.buffer_pcb), sizeof(buffer));
            
            if ( lost_packet_count == 11 ) {
                
                last_lost_packet = current_seq;
                j = check_final;
                
            }
            
        }
        
        if ( lost_packet_count == 11 ) {
            printf("|| Max packet loss limit reached, resetting connection here.\n" );
            printf("|| Last successful packet : %d\n", last_lost_packet);
            printf("|| File send FAILED\n\n");
        } else {
            printf(">> File send COMPLETE\n\n");
            srtp_close(&x);
        }
        //calls srtp_close to terminate the client program
        // printf("the program is now shutting down...\n");
    }
}
/*
    Function    : file_read
    Purpose     : Reads the portion of the file from addr_lastpt into the buffer for sending
    Parameter   : addr to start reading = addr_lastpt
 */

int file_read(int addr_lastpt){
    
    pFile = fopen(filename,"r" ); // open the file and read it
    if (pFile == NULL){ 
        //error checking
    	perror("|| On file_read fopen");
        exit(EXIT_FAILURE);
    }
    
    fseek(pFile, addr_lastpt, SEEK_SET);
    //read in the file to buffer, which has the srtp_max_send_buf_size
    fread(buffer,1,SRTP_MAX_SEND_BUF_SIZE,pFile);
    //buffer[SRTP_MAX_SEND_BUF_SIZE]= 0;
    strncpy(x.buffer_pcb, buffer, SRTP_MAX_SEND_BUF_SIZE);
    fclose(pFile);
    
    return htonl((long unsigned)buffer);
}
